package com.umeox.watch.moto.dataservice.receiver;


import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.SystemClock;

import com.umeox.moto.common.log.Logger;
import com.umeox.moto.common.utils.DateUtils;
import com.umeox.moto.common.utils.StringUtils;
import com.umeox.watch.moto.dataservice.DataProvider;
import com.umeox.watch.moto.dataservice.constants.Constants;
import com.umeox.watch.moto.dataservice.constants.InternalConstant;
import com.umeox.watch.moto.dataservice.db.AppDataBase;
import com.umeox.watch.moto.dataservice.db.model.LocationPeriod;
import com.umeox.watch.moto.dataservice.services.LocationService;
import com.umeox.watch.moto.dataservice.utils.CareTimeUtil;
import com.umeox.watch.moto.dataservice.utils.LogUtils;
import com.umeox.watch.moto.dataservice.utils.MMKVUtils;
import com.umeox.watch.moto.dataservice.utils.Utils;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;

public class LocationReceiver extends BroadcastReceiver {
    public static final String EXTRA_OFFSET = "offset";


    private static final Pattern REPEAT_EXP_PATTERN = Pattern.compile("[0-1]{7}");
    private boolean isLocationPeriodStartTime;

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Logger.i("LocationReceiver Broadcast >>>" + action);
        LogUtils.saveLog("LocationReceiver Broadcast >>>" + action);
        if (InternalConstant.ACTION_RESET_LOCATION_ALARM.equals(action)) {
            long offset = intent.getLongExtra(EXTRA_OFFSET, 0);
            Logger.d("重置定位闹钟[offset:" + offset + "]");
            LogUtils.saveLog("重置定位闹钟[offset:" + offset + "]");
            isLocationPeriodStartTime = false;
            HashMap<Long, Long> result = getNextFreqLocationTimeMillis();
            long nextTime4Concern = 0;
            long locFreqId = 0;
            if (result != null) {
                for (Long key : result.keySet()) {
                    locFreqId = key;
                    nextTime4Concern = result.get(key);
                }
            }

            if (!isLocationPeriodStartTime) {
                nextTime4Concern = (nextTime4Concern - offset) <= System.currentTimeMillis() ? nextTime4Concern : (nextTime4Concern - offset);
            }

            if (DataProvider.isActivated() && nextTime4Concern > 0) {
                //根据特殊关爱时段定位频率定位
                scheduleLocationAlarm(context, nextTime4Concern, locFreqId);
            } else {
                cancelLocationAlarm(context);
            }
        } else if (InternalConstant.ACTION_REQUEST_LOCATION_ALARM.equals(action)) {
            if (!DataProvider.isActivated()) {
                Logger.d("设备未激活，忽略频率定位请求");
                return;
            }
            //PengdingIntent请求定位，定位完成再设置下次定位闹钟
            long locFreqId = intent.getLongExtra(InternalConstant.EXTRA_LOC_ID, 0);
            LocationService.freqLocation(context, locFreqId);
        } else if (Constants.ACTION_UNBIND.equals(action)) {
            //解绑设备的时候调用这里
            //取消定位闹钟
            cancelLocationAlarm(context);
        } else if (Constants.ACTION_HANG_UP.equals(intent.getAction())) {
            if (!DataProvider.isActivated()) {
                Logger.d("设备未激活，忽略通话定位请求");
                return;
            }
            if (!DataProvider.isReportCallLocation()) {
                Logger.d("APP已关闭通话定位，忽略通话定位请求");
                return;
            }
            //手表通话结束定位
            String name = intent.getStringExtra(Constants.EXTRA_CONTACT_NAME);
            int type = intent.getIntExtra(Constants.EXTRA_CALL_TYPE, 0);
            String phoneNumber = intent.getStringExtra(Constants.EXTRA_PHONE_NUMBER);
            long time = intent.getLongExtra(Constants.EXTRA_OFFHOOK_TIME, 0);
            int dur = intent.getIntExtra(Constants.EXTRA_CALL_DURATION, 0);
            String reportReasonAttr = phoneNumber + "#" + type + "#" + name + "#" + time + "#" + dur;
            Logger.d("通话结束定位请求>>>" + reportReasonAttr);
            if (dur > 0) {
                //有通话时间上报位置
                LocationService.callEndedLocation(context, reportReasonAttr);
            }
        }
    }

    /**
     * 判断时间段，重复周期格式是否正确
     * 正确格式：1100011  7个字节长度，只允许0或者1
     */
    private boolean isValidRepeatExpression(String repeatExpression) {
        return !StringUtils.isEmpty(repeatExpression)
                && !"0000000".equals(repeatExpression)
                && REPEAT_EXP_PATTERN.matcher(repeatExpression).matches();
    }

    /**
     * @param mss 要转换的毫秒数
     * @return 该毫秒数转换为days:hours:minutes:seconds 后的格式
     */
    private String formatDuring(long mss) {
        String format = "%1$sd:%2$sh:%3$sm:%4$ss";
        long days = mss / (1000 * 60 * 60 * 24);
        long hours = (mss % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60);
        long minutes = (mss % (1000 * 60 * 60)) / (1000 * 60);
        long seconds = (mss % (1000 * 60)) / 1000;
        return String.format(Locale.getDefault(), format, days, hours, minutes, seconds);
    }

    /**
     * 获取下次定位的时间
     *
     * @param sec        时间段，例如:["08","30","17","30"], [小时，分钟，小时，分钟]
     * @param expression 时间段，重复周期参数表示，例[1,1,0,0,0,1,1]，表示每周按星期天，星期一，星期五，星期六计算
     * @param frequency  定位频率，单位为秒
     */
    private long getNextLocateTime(String[] sec, char[] expression, int frequency) {
        Calendar temp = Calendar.getInstance();
        Calendar timeOn = Calendar.getInstance();
        Calendar timeOff = Calendar.getInstance();

        int hourOfDay1 = Integer.parseInt(sec[0]);
        int minute1 = Integer.parseInt(sec[1]);
        timeOn.set(Calendar.HOUR_OF_DAY, hourOfDay1);
        timeOn.set(Calendar.MINUTE, minute1);
        timeOn.set(Calendar.SECOND, 0);
        timeOn.set(Calendar.MILLISECOND, 0);

        int hourOfDay2 = Integer.parseInt(sec[2]);
        int minute2 = Integer.parseInt(sec[3]);
        timeOff.set(Calendar.HOUR_OF_DAY, hourOfDay2);
        timeOff.set(Calendar.MINUTE, minute2);
        timeOff.set(Calendar.SECOND, 0);
        timeOff.set(Calendar.MILLISECOND, 0);

        long nextTime = 0;
        boolean cycleFlag = true;
        int currentDayOfWeek = temp.get(Calendar.DAY_OF_WEEK);//当前是星期几

        while (cycleFlag) {
            //timeOn/timeOff calendar不一定是今天，可能是今天+N天，N一定小于等于7
            //因为前面已经对RepeatExpression表达式做判断，星期一到星期天肯定有一天设置了特殊关爱时段
            //获取timeOn/timeOff是周几
            int tempDayOfWeek = timeOn.get(Calendar.DAY_OF_WEEK);
            //当天是否开启
            boolean opened = "1".equals(String.valueOf(expression[tempDayOfWeek - 1]));
            if (opened) {
                //如果tempDayOfWeek不是今天，nextTime等于currentDayOfWeek +ｎ当天特殊关爱时段的开始时刻
                if (tempDayOfWeek - currentDayOfWeek > 0) {
                    nextTime = timeOn.getTimeInMillis();
                    cycleFlag = false;
                } else {
                    //下面代码只会执行一次
                    //当天就需要判断(现在时间+定位频率)跟当前时刻是否在定位时段内
                    //当前时刻在定位时段之前
                    long tempWithoutFreq = temp.getTimeInMillis();
                    temp.add(Calendar.SECOND, frequency);
                    long tempWithFreq = temp.getTimeInMillis();
                    /*
                    LogUtility.e("tempDayOfWeek:" + tempDayOfWeek + "|currentDayOfWeek:" + currentDayOfWeek
                            + "----tempWithoutFreq:" + tempWithoutFreq + "|tempWithFreq:" + tempWithFreq
                            + "----timeOnMillis:" + timeOn.getTimeInMillis() + "|timeOffMillis:" + timeOff.getTimeInMillis());
                    */
                    //当前时刻在时段之前并且加上定位频率后任然在，取时段开始点
                    if (tempWithFreq < timeOn.getTimeInMillis()) {
                        nextTime = timeOn.getTimeInMillis();
                        break;
                    }
                    //当前时刻在时段开始之前但是加上定位频率后在时段开始之后，取时段开始点
                    else if (tempWithoutFreq < timeOn.getTimeInMillis() && tempWithFreq >= timeOn.getTimeInMillis()) {
                        nextTime = timeOn.getTimeInMillis();
                        break;
                    }
                    //当前时刻在时段内并且加上定位频率后任然在，取当前时间加上定位频率后的时间
                    else if (tempWithoutFreq >= timeOn.getTimeInMillis()
                            && tempWithoutFreq <= timeOff.getTimeInMillis()
                            && tempWithFreq <= timeOff.getTimeInMillis()) {
                        nextTime = tempWithFreq;
                        break;
                    }
                    //当前时刻在时段内但是加上定位频率后不在时段内，取时段结束点
                    else if (tempWithoutFreq >= timeOn.getTimeInMillis()
                            && tempWithoutFreq <= timeOff.getTimeInMillis()
                            && tempWithFreq > timeOff.getTimeInMillis()) {
                        nextTime = timeOff.getTimeInMillis();
                        break;
                    }
                }
            }
            timeOn.add(Calendar.DAY_OF_MONTH, 1);
            timeOff.add(Calendar.DAY_OF_MONTH, 1);
        }
        Logger.w(String.format(Locale.getDefault(), "current time:%1$s, nextTime: %2$s, freq: %3$d",
                DateUtils.formatDateTime(System.currentTimeMillis()),
                DateUtils.formatDateTime(nextTime),
                frequency));
        LogUtils.saveLog(String.format(Locale.getDefault(), "current time:%1$s, nextTime: %2$s, freq: %3$d",
                DateUtils.formatDateTime(System.currentTimeMillis()),
                DateUtils.formatDateTime(nextTime),
                frequency));

        return nextTime;
    }

    /**
     * 获取特殊关爱时段下次定位的时间
     *
     * @return
     */
    private HashMap<Long, Long> getNextFreqLocationTimeMillis() {
        //根据当前时间查询出定位时段
        LocationPeriod locationPeriod = null;
        try {
            long currentTime = System.currentTimeMillis();
            List<LocationPeriod> locationPeriodList = AppDataBase.getInstance().locationPeriodDao().query(DateUtils.formatTime(currentTime));
            if (!locationPeriodList.isEmpty()) {
                for (LocationPeriod period : locationPeriodList) {
                    if(currentTime > DateUtils.formatDateToTime(period.getEndTime())){
                        //当前时间已经超过了按分钟匹配的结果
                        continue;
                    }
                    char[] repeatExpression = period.getRepeatExpression().toCharArray();
                    int currentDayOfWeek = Calendar.getInstance().get(Calendar.DAY_OF_WEEK);//当前是星期几
                    //当天是否开启
                    boolean opened = "1".equals(String.valueOf(repeatExpression[currentDayOfWeek - 1]));
                    if (opened) {
                        locationPeriod = period;
                    }
                }
            }
        } catch (Exception e) {
            LogUtils.saveLog("从数据库中取关爱时段报错，即将从MMKV缓存中取值");
            //从MMKV缓存里面取
            List<LocationPeriod> list = MMKVUtils.getArray("location_period", new LocationPeriod());
            if (!list.isEmpty()) {
                for (LocationPeriod period : list) {
                    Logger.e(period.toString());
                    long currentTimeMillis = System.currentTimeMillis();
                    long startTime = DateUtils.formatDateToTime(period.getStartTime());
                    long endTime = DateUtils.formatDateToTime(period.getEndTime());
                    char[] repeatExpression = period.getRepeatExpression().toCharArray();
                    int currentDayOfWeek = Calendar.getInstance().get(Calendar.DAY_OF_WEEK);//当前是星期几
                    //当天是否开启
                    boolean opened = "1".equals(String.valueOf(repeatExpression[currentDayOfWeek - 1]));
                    Logger.e("currentTimeMillis = " + currentTimeMillis + "，startTime=" + startTime + "，endTime=" + endTime);
                    if (currentTimeMillis >= startTime && currentTimeMillis <= endTime && opened) {
                        locationPeriod = period;
                        break;
                    }
                }
            }
        }

        if (locationPeriod == null) {
            //当前时间不在任何一个时间设置的关爱时间段内，需要找到一个与当前时间段最近的关爱时段
            List<LocationPeriod> locationPeriodList;
            try {
                locationPeriodList = AppDataBase.getInstance().locationPeriodDao().queryAll();
            } catch (Exception e) {
                LogUtils.saveLog("从数据库中取关爱时段报错，即将从MMKV缓存中取值");
                //从MMKV缓存里面取
                locationPeriodList = MMKVUtils.getArray("location_period", new LocationPeriod());
            }

            if (locationPeriodList.size() == 1) {
                locationPeriod = locationPeriodList.get(0);
            } else {
                locationPeriod = CareTimeUtil.findNextBean(locationPeriodList);
            }
        }

        if (locationPeriod == null) {
            return null;
        }

        String[] timeRange = Utils.parsePeriod(String.format("%s-%s", locationPeriod.getStartTime(), locationPeriod.getEndTime()));
        if (timeRange == null) {
            return null;
        }
        Logger.e(locationPeriod.toString());
        int frequency = locationPeriod.getFreq();
        long locFreqId = locationPeriod.getId();

        if (frequency <= 0) {
            return null;
        }
        /*
         * repeatExpression,重复周期参数表示（仅按周重复有效），例1100011，表示每周按星期天，星期一，星期五，星期六计算
         * 字符长度小于7，不是0或者1的其他字符，全部为0都无效
         */
        String repeatExpression = locationPeriod.getRepeatExpression();
        if (!isValidRepeatExpression(repeatExpression)) {
            return null;
        }

        char[] c = repeatExpression.toCharArray();
        long nextLocTime = getNextLocateTime(timeRange, c, frequency);
        String hhMM = DateUtils.formatDateTime(nextLocTime, "HH:mm");
        Logger.e("hhMM=" + hhMM);
        if (hhMM.equals(locationPeriod.getStartTime())) {
            Logger.e("下次定位时间为关爱时段的开始时间");
            isLocationPeriodStartTime = true;
        }
        HashMap<Long, Long> result = new HashMap<>();
        result.put(locFreqId, nextLocTime);
        return result;
    }

    private LocationPeriod getNextLocationPeriod(List<LocationPeriod> tempLocationPeriodList) {
        if (tempLocationPeriodList == null || tempLocationPeriodList.isEmpty()) {
            Logger.w("所有关爱时段内当天定位开关都是关闭状态");
            LocationPeriod result = null;
            List<LocationPeriod> locationPeriodList;
            try {
                locationPeriodList = AppDataBase.getInstance().locationPeriodDao().queryAll();
            } catch (Exception e) {
                LogUtils.saveLog("从数据库中取关爱时段报错，即将从MMKV缓存中取值");
                //从MMKV缓存里面取
                locationPeriodList = MMKVUtils.getArray("location_period", new LocationPeriod());
            }

            if (locationPeriodList != null && !locationPeriodList.isEmpty()) {
                result = locationPeriodList.get(0);
            }
            return result;
        }

        List<LocationPeriod> temp = new ArrayList<>();
        for (LocationPeriod period : tempLocationPeriodList) {
            Logger.e("period=" + period.toString());
            if (DateUtils.formatDateToTime(period.getStartTime()) > System.currentTimeMillis()) {
                temp.add(period);
            }
        }

        if (temp.isEmpty()) {
            //TODO
            return tempLocationPeriodList.get(0);
        }

        LocationPeriod nextLocationPeriod = null;
        for (LocationPeriod period : temp) {
            Logger.i("每个关爱时段，重复周期中距离现在最近是：" + period.toString());
            if (nextLocationPeriod == null
                    || DateUtils.formatDateToTime(nextLocationPeriod.getStartTime()) > DateUtils.formatDateToTime(period.getStartTime())) {
                nextLocationPeriod = period;
            }
        }
        return nextLocationPeriod;
    }

    /**
     * 设置定位闹钟
     *
     * @param nextTime  闹钟执行时间
     * @param locFreqId
     */
    private void scheduleLocationAlarm(Context ctx, long nextTime, long locFreqId) {
        if (nextTime <= 0) {
            cancelLocationAlarm(ctx);
            return;
        }
        long elapsedRealtime = SystemClock.elapsedRealtime();
        long realNextTime = elapsedRealtime + nextTime - System.currentTimeMillis();
        Logger.i("当前已开机时长->" + formatDuring(elapsedRealtime));
        LogUtils.saveLog("当前已开机时长->" + formatDuring(elapsedRealtime));
        Logger.i("下次频率定位时间->" + DateUtils.formatDateTime(nextTime) + "|" + formatDuring(realNextTime));
        LogUtils.saveLog("下次频率定位时间->" + DateUtils.formatDateTime(nextTime) + "|" + formatDuring(realNextTime));

        Intent intent = new Intent(ctx, LocationReceiver.class);
        intent.setAction(InternalConstant.ACTION_REQUEST_LOCATION_ALARM);
        intent.putExtra(InternalConstant.EXTRA_LOC_ID, locFreqId);
        AlarmManager alm = ((AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE));
        PendingIntent pendingIntent = PendingIntent.getBroadcast(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        if (isLocationPeriodStartTime) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                alm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTime, pendingIntent);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                alm.setExact(AlarmManager.RTC_WAKEUP, nextTime, pendingIntent);
            } else {
                alm.set(AlarmManager.RTC_WAKEUP, nextTime, pendingIntent);
            }
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                alm.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, realNextTime, pendingIntent);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                alm.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, realNextTime, pendingIntent);
            } else {
                alm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, realNextTime, pendingIntent);
            }
        }

    }

    /**
     * 取消定位闹钟
     */
    private void cancelLocationAlarm(Context ctx) {
        Logger.i("取消已设定的频率定位闹钟");
        Intent intent = new Intent(ctx, LocationReceiver.class);
        intent.setAction(InternalConstant.ACTION_REQUEST_LOCATION_ALARM);
        AlarmManager alm = ((AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE));
        PendingIntent pendingIntent = PendingIntent.getBroadcast(ctx, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_NO_CREATE);
        if (pendingIntent != null) {
            alm.cancel(pendingIntent);
        }
    }

}
